package com.study.controller;


import com.alibaba.fastjson.JSON;
import com.study.service.NacosClientService;
import com.study.service.hystrix.*;
import com.study.service.hystrix.ruquest_merge.NacosClientCollapseCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.web.bind.annotation.*;
import rx.Observable;
import rx.Observer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

@Slf4j
@RestController
@RequestMapping("/hystrix")
public class HystrixController {


    @Autowired
    private NacosClientService nacosClientService;

    @Autowired
    private CacheHystrixCommandAnnotation cacheHystrixCommandAnnotation;

    @Autowired
    private UseHystrixCommandAnnotation useHystrixCommandAnnotation;

    @GetMapping("/hystrix-command-annotation")
    public List<ServiceInstance> getUseHystrixCommandAnnotation(@RequestParam String serviceId){

        log.info("getUseHystrixCommandAnnotation、serviceId: {},线程名字：{}",
                serviceId,Thread.currentThread().getName());
        return useHystrixCommandAnnotation.getNacosClientInfo(serviceId);
    };

    @GetMapping("/simple-hystrix-command")
    public List<ServiceInstance> getServiceInstanceByServiceId(
            @RequestParam String serviceId) throws ExecutionException, InterruptedException {

        //方式一
        List<ServiceInstance> servicerInstance01 = new NacosClientHystrixCommand(
                nacosClientService, serviceId
        ).execute(); //同步阻塞

        log.info("use execute to get service instance: [{}],[{}]",
                JSON.toJSONString(servicerInstance01), Thread.currentThread().getName());

        //方式二 最常用的方式
        List<ServiceInstance> servicerInstance02;
        Future<List<ServiceInstance>> future = new NacosClientHystrixCommand(
                nacosClientService, serviceId
        ).queue(); //异步非阻塞
        //这里可以做一些其他的事情、需要时候再去拿结果
        servicerInstance02 = future.get();
        log.info("use queue to get service instance: [{}],[{}]",
                JSON.toJSONString(servicerInstance02), Thread.currentThread().getName());


        //方式三
        Observable<List<ServiceInstance>> observable = new NacosClientHystrixCommand(
                nacosClientService, serviceId
        ).observe(); //热响应调用

        List<ServiceInstance> servicerInstance03 = observable.toBlocking().single();

        log.info("use observe to get service instance: [{}],[{}]",
                JSON.toJSONString(servicerInstance03), Thread.currentThread().getName());


        //方式四
        Observable<List<ServiceInstance>> toObservable = new NacosClientHystrixCommand(
                nacosClientService, serviceId
        ).toObservable(); //异步冷响应调用

        List<ServiceInstance> servicerInstance04 = toObservable.toBlocking().single();

        log.info("use toObservable to get service instance: [{}],[{}]",
                JSON.toJSONString(servicerInstance04), Thread.currentThread().getName());

        return servicerInstance01;
    }

    @GetMapping("/simple-observable-command")
    public List<ServiceInstance> getServiceIntanceByObservable(
            @RequestParam String serviceId) {

        List<String> serviceIds = Arrays.asList(serviceId, serviceId, serviceId);
        List<List<ServiceInstance>> result = new ArrayList<>();
        NacosClientHystrixObservableCommand observableCommand = new NacosClientHystrixObservableCommand(nacosClientService, serviceIds);

        Observable<List<ServiceInstance>> observable = observableCommand.observe();

        observable.subscribe(
                new Observer<List<ServiceInstance>>() {
                    @Override
                    public void onCompleted() {
                        log.info("all task is complete：[{}],[{}]",
                                serviceId, Thread.currentThread().getName());
                    }

                    @Override
                    public void onError(Throwable e) {
                        e.printStackTrace();
                    }

                    @Override
                    public void onNext(List<ServiceInstance> instances) {
                        result.add(instances);
                    }
                }
        );

        log.info("observable command result is : [{}],[{}]",
                JSON.toJSONString(result)
                , Thread.currentThread().getName());
        return result.get(0);
    }

    @GetMapping("/cache-hystrix-command")
    public List<ServiceInstance> cacheHystrixCommand(@RequestParam String serviceId) {

        //使用 缓存 command、发起两次请求
        CacheHystrixCommand command01 = new CacheHystrixCommand(
                nacosClientService, serviceId
        );
        CacheHystrixCommand command02 = new CacheHystrixCommand(
                nacosClientService, serviceId
        );

        List<ServiceInstance> result01 = command01.execute();
        List<ServiceInstance> result02 = command02.execute();

        log.info("result01,result02; [{}],[{}]",
                JSON.toJSONString(result01)
                , JSON.toJSONString(result02));

        //清除缓存
        CacheHystrixCommand.flushRequestCache(serviceId);


        CacheHystrixCommand command03 = new CacheHystrixCommand(
                nacosClientService, serviceId
        );
        CacheHystrixCommand command04 = new CacheHystrixCommand(
                nacosClientService, serviceId
        );

        List<ServiceInstance> result03 = command03.execute();
        List<ServiceInstance> result04 = command04.execute();

        log.info("result03,result04; [{}],[{}]",
                JSON.toJSONString(result03)
                , JSON.toJSONString(result04));

        return result01;

    }

    @GetMapping("/cache-annotation-01")
    public List<ServiceInstance> useCacheByAnnotation(@RequestParam String serviceId) {
        log.info("use cache by annotation01-controller,[{}]",
                serviceId);

        List<ServiceInstance> result01 =
                cacheHystrixCommandAnnotation.useCacheByAnnotation01(serviceId);
        List<ServiceInstance> result02 =
                cacheHystrixCommandAnnotation.useCacheByAnnotation01(serviceId);

        cacheHystrixCommandAnnotation.flushCacheByAnnotation01(serviceId);

        List<ServiceInstance> result03 =
                cacheHystrixCommandAnnotation.useCacheByAnnotation01(serviceId);
        return cacheHystrixCommandAnnotation.useCacheByAnnotation01(serviceId);
    }

    @GetMapping("/cache-annotation-02")
    public List<ServiceInstance> useCacheByAnnotation02(@RequestParam String serviceId) {
        log.info("use cache by annotation02-controller,[{}]",
                serviceId);

        List<ServiceInstance> result01 =
                cacheHystrixCommandAnnotation.useCacheByAnnotation02(serviceId);
        List<ServiceInstance> result02 =
                cacheHystrixCommandAnnotation.useCacheByAnnotation02(serviceId);

        cacheHystrixCommandAnnotation.flushCacheByAnnotation02(serviceId);

        List<ServiceInstance> result03 =
                cacheHystrixCommandAnnotation.useCacheByAnnotation02(serviceId);
        return cacheHystrixCommandAnnotation.useCacheByAnnotation02(serviceId);
    }

    @GetMapping("/cache-annotation-03")
    public List<ServiceInstance> useCacheByAnnotation03(@RequestParam String serviceId) {
        log.info("use cache by annotation03-controller,[{}]",
                serviceId);

        List<ServiceInstance> result01 =
                cacheHystrixCommandAnnotation.useCacheByAnnotation03(serviceId);
        List<ServiceInstance> result02 =
                cacheHystrixCommandAnnotation.useCacheByAnnotation03(serviceId);

        cacheHystrixCommandAnnotation.flushCacheByAnnotation03(serviceId);

        List<ServiceInstance> result03 =
                cacheHystrixCommandAnnotation.useCacheByAnnotation03(serviceId);
        return cacheHystrixCommandAnnotation.useCacheByAnnotation03(serviceId);
    }

    //请求合并
    @GetMapping("/request-merge")
    public void questMerge() throws Exception {

        //前三个请求会被合并
        NacosClientCollapseCommand collapseCommand01 = new NacosClientCollapseCommand(
                nacosClientService, "ecommerce-nacos-client01"
        );
        NacosClientCollapseCommand collapseCommand02 = new NacosClientCollapseCommand(
                nacosClientService, "ecommerce-nacos-client02"
        );
        NacosClientCollapseCommand collapseCommand03 = new NacosClientCollapseCommand(
                nacosClientService, "ecommerce-nacos-client03"
        );

        Future<List<ServiceInstance>> future01 = collapseCommand01.queue();
        Future<List<ServiceInstance>> future02 = collapseCommand02.queue();
        Future<List<ServiceInstance>> future03 = collapseCommand03.queue();

        future01.get();
        future02.get();
        future03.get();

        Thread.sleep(2000);

        //过期合并的时间窗口withTimerDelayInMilliseconds(300)
        //第四个请求单独发起
        NacosClientCollapseCommand collapseCommand04 = new NacosClientCollapseCommand(
                nacosClientService, "ecommerce-nacos-client04"
        );
        Future<List<ServiceInstance>> future04 = collapseCommand04.queue();
        future04.get();
    }

    //注解方式-请求合并
    @GetMapping("/request-merge-annotation")
    public void requestMergeAnnotation() throws ExecutionException, InterruptedException {

        Future<List<ServiceInstance>> future01 = nacosClientService.findNacosClientInfo(
                "ecommerce-nacos-client01"
        );
        Future<List<ServiceInstance>> future02 = nacosClientService.findNacosClientInfo(
                "ecommerce-nacos-client02"
        );
        Future<List<ServiceInstance>> future03 = nacosClientService.findNacosClientInfo(
                "ecommerce-nacos-client03"
        );

        future01.get();
        future02.get();
        future03.get();

        Thread.sleep(2000);

        Future<List<ServiceInstance>> future04 = nacosClientService.findNacosClientInfo(
                "ecommerce-nacos-client04"
        );
        future04.get();


    }
}
